﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Windows;
using System.Windows.Interop;
using BuzzIO;

namespace VideoReflector.Controls
{
    /// <summary>
    /// Defines a simple base WPF window class that registers itself for device notifications.
    /// Based on WinForms based UsbAwareForm class in Buzz controller demo app by Ashley Deakin
    /// See article at http://www.developerfusion.com/article/84338/making-usb-c-friendly/
    /// </summary>
    public class UsbAwareWindow : Window, IDisposable
    {
        #region Private fields

        /// <summary>HID Guid</summary>
        private Guid _deviceClassGuid;

        /// <summary>Handle returned by RegisterForUsbEvents - need it when we unregister</summary>
        private IntPtr _usbEventHandle;
        
        #endregion // Private fields

        #region Constructor

        /// <summary>
		/// Constructor. No handle yet so can't register for USB events here
		/// </summary>
        public UsbAwareWindow()
		{
			_deviceClassGuid = Win32Usb.HIDGuid;
        }

        #endregion // Constructor

        #region Public properties

        /// <summary>
        /// Accessor for device class guid
        /// </summary>
        public Guid DeviceClassGuid
        {
            get
            {
                return _deviceClassGuid;
            }
        }

        #endregion // Public properties

        #region Events

        /// <summary>Event called when a new device is detected</summary>
        public event EventHandler DeviceArrived;

        /// <summary>Event called when a device is removed</summary>
        public event EventHandler DeviceRemoved;

        #endregion // Events

        #region Virtual methods

        /// <summary>
        /// Overridable 'On' method called when a new device is detected
        /// </summary>
        protected virtual void OnDeviceArrived(EventArgs args)
        {
            if (DeviceArrived != null)
            {
                DeviceArrived(this, args);
            }
        }
        /// <summary>
        /// Overridable 'On' method called when a device is removed
        /// </summary>
        protected virtual void OnDeviceRemoved(EventArgs args)
        {
            if (DeviceRemoved != null)
            {
                DeviceRemoved(this, args);
            }
        }

        #endregion // Virtual methods

        #region Hwnd message handling

        /// <summary>
        /// Register once initialized
        /// </summary>
        protected override void OnSourceInitialized(EventArgs e)
        {
            base.OnSourceInitialized(e);
            HwndSource source = PresentationSource.FromVisual(this) as HwndSource;
            source.AddHook(WndProc);
            _usbEventHandle = Win32Usb.RegisterForUsbEvents(source.Handle, _deviceClassGuid);
        }

        /// <summary>
        /// Handle incoming Windows messages.
        /// </summary>
        private IntPtr WndProc(IntPtr hwnd, int msg, IntPtr wParam, IntPtr lParam, ref bool handled)
        {
            if (msg == Win32Usb.WM_DEVICECHANGE)	// we got a device change message! A USB device was inserted or removed
            {
                switch (wParam.ToInt32())	// Check the W parameter to see if a device was inserted or removed
                {
                    case Win32Usb.DEVICE_ARRIVAL:	// inserted
                        OnDeviceArrived(new EventArgs());
                        break;
                    case Win32Usb.DEVICE_REMOVECOMPLETE:	// removed
                        OnDeviceRemoved(new EventArgs());
                        break;
                }
            }
            return IntPtr.Zero;
        }

#endregion // Hwnd message handling

        #region IDisposable implementation

        private bool _isDisposed;

        /// <summary>
        /// Dispose cleanly of unmanaged resources referenced by object
        /// </summary>
        public void Dispose()
        {
            if (!_isDisposed)
            {
                Dispose(true);
                GC.SuppressFinalize(this);
                _isDisposed = true;
            }
        }

        /// <summary>
        /// Clean up any resources being used.
        /// </summary>
        /// <param name="disposing">true if managed resources should be disposed; otherwise, false.</param>
        protected virtual void Dispose(bool disposing)
        {
            Win32Usb.UnregisterForUsbEvents(_usbEventHandle);
        }

        /// <summary>
        /// Dispose of resource when finalizing if not already done so
        /// </summary>
        ~UsbAwareWindow()
        {
            if (!_isDisposed)
            {
                Dispose(false);
                _isDisposed = true;
            }
        }

      /// <summary>
      /// Throw an ObjectDisposedException if current instance has been disposed
      /// </summary>
      protected void VerifyObjectNotDisposed()
      {
         if (_isDisposed)
         {
            throw new ObjectDisposedException(this.GetType().Name);
         }
      }

      #endregion // IDisposable implementation
    }
}
